using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;

using Pacman.Simulator;
using Pacman.Simulator.Ghosts;
using NeuralNetwork;

namespace Pacman.Implementations
{
	public class NeuralDangerPac : BasePacman
	{
		public NeuralDangerPac() : base("NeuralDangerPac","1") { }

		Network network = null;
		List<DirectionDanger> danger;
		GameState gs;

		public override Direction Think(GameState gs) {
			this.gs = gs;
			if( network == null ) {
				network = Network.Load("danger.nn");
			}
			danger = getDanger(gs);
			List<DirectionDanger> possibleDanger = getPossibleDanger(danger);
			Direction bestDirection = possibleDanger[0].Direction;
			// just take shortest path if it's somewhat safe
			StateInfo.PillPath globalPillPath = StateInfo.NearestPill(gs.Pacman.Node, gs);
			if( globalPillPath.PathInfo != null ) {
				foreach( DirectionDanger dd in possibleDanger ) {
					if( dd.Direction == globalPillPath.PathInfo.Direction ) {
						if( dd.Danger < 0.1 ) {
							Console.WriteLine("Pill: " + dd.Direction + " - " + dd.Danger);
							return globalPillPath.PathInfo.Direction;
						}
					}
				}
			}
			return bestDirection;
		}

		public override void Draw(System.Drawing.Graphics g) {
			foreach( DirectionDanger dd in danger ) {
				int green = 255;
				int red = 0;
				if( dd.Danger < 0.5 ){
					red += (int)(dd.Danger * 510);
				} else{
					red = 255;
					green -= (int)(dd.Danger * 255);
				}
				SolidBrush brush = new SolidBrush(Color.FromArgb(100,red,green,0));
				const int size = 50;
				const int offset = 70;
				if( dd.Direction == Direction.Up ) {
					g.FillEllipse(brush, new Rectangle(new Point(gs.Map.PixelWidth / 2 - size / 2, gs.Map.PixelHeight / 2 - offset), new Size(size, size)));
				}
				if( dd.Direction == Direction.Down ) {
					g.FillEllipse(brush, new Rectangle(new Point(gs.Map.PixelWidth / 2 - size / 2, gs.Map.PixelHeight / 2 + (offset - size)), new Size(size, size)));
				}
				if( dd.Direction == Direction.Left ) {
					g.FillEllipse(brush, new Rectangle(new Point(gs.Map.PixelWidth / 2 - offset, gs.Map.PixelHeight / 2 - size / 2), new Size(size, size)));
				}
				if( dd.Direction == Direction.Right ) {
					g.FillEllipse(brush, new Rectangle(new Point(gs.Map.PixelWidth / 2 + (offset - size), gs.Map.PixelHeight / 2 - size / 2), new Size(size, size)));
				}
			}
		}

		private List<DirectionDanger> getPossibleDanger(List<DirectionDanger> danger) {
			List<DirectionDanger> possibleDanger = new List<DirectionDanger>();
			foreach( DirectionDanger dd in danger ) {
				if( dd.Possible ) {
					possibleDanger.Add(dd);
				}
			}
			return possibleDanger;
		}

		private List<DirectionDanger> getDanger(GameState gs) {
			List<DirectionDanger> directionDanger = new List<DirectionDanger>();
			Direction[] allDirections = { Direction.Up, Direction.Down, Direction.Left, Direction.Right };
			foreach( Direction direction in allDirections ) {
				double[] inputs = new double[8];
				for( int i = 0; i < 4; i++ ) {
					Ghost ghost = gs.Ghosts[i];
					// angles to pacman				
					double angleToPacman = Math.Atan2(ghost.Node.Y - gs.Pacman.Y, ghost.Node.X - gs.Pacman.X);
					double angle = (angleToPacman + Math.PI) / (2 * Math.PI);
					if( direction == Direction.Right ) {
						angle += 0.25;
					} else if( direction == Direction.Left ) {
						angle -= 0.25;
					} else if( direction == Direction.Down ) {
						angle -= 0.5;
					}
					if( angle < 0.0 ) {
						angle = 1.0 + angle;
					}
					if( angle > 1.0 ) {
						angle -= 1.0;
					}
					inputs[i * 2] = angle;
					// distance to pacman
					Node.PathInfo path = gs.Pacman.Node.ShortestPath[ghost.Node.X, ghost.Node.Y];
					int distance = 40;
					if( path != null ) {
						distance = path.Distance;
					}
					// add to inputs
					inputs[i * 2 + 1] = distance / 40.0;
				}
				bool possible = false;
				foreach( Direction possibleDir in gs.Pacman.PossibleDirections() ) {
					if( possibleDir == direction ) {
						possible = true;
						break;
					}
				}
				double danger = network.GetOutputs(inputs)[0];
				directionDanger.Add(new DirectionDanger(direction, danger, possible));
			}
			directionDanger.Sort(new Comparison<DirectionDanger>(delegate(DirectionDanger dd1, DirectionDanger dd2) {				
				if( dd1.Danger == dd2.Danger ) return 0;
				if( dd1.Danger < dd2.Danger ) return -1;
				return 1;
			}));
			return directionDanger;
		}

		private class DirectionDanger
		{
			public readonly Direction Direction;
			public readonly double Danger;
			public readonly bool Possible;

			public DirectionDanger(Direction direction, double danger, bool possible) {
				this.Direction = direction;
				this.Danger = danger;
				this.Possible = possible;
			}
		}
	}
}
